package kv

import (
	"database/sql"
	"gitee.com/tziye/renai-go/common"
	"gitee.com/tziye/renai-go/renai"
	"gitee.com/tziye/renai-go/sqlproxy"
)

type PriorityQOperator struct {
	RenaiClient *renai.RenaiClient
	Db          *sql.DB
}

func NewPriorityQOperator(renaiClient *renai.RenaiClient) *PriorityQOperator {
	return &PriorityQOperator{
		RenaiClient: renaiClient,
		Db:          renaiClient.Db,
	}
}

func (priorityQOperator *PriorityQOperator) SaveKey(key string, expireSeconds int) {
	saveKey(priorityQOperator.Db, key, expireSeconds, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Key(key string) RenaiKey {
	return queryKey(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Keys(keys []string) []RenaiKey {
	return queryKeys(priorityQOperator.Db, keys, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) AllKeys() []RenaiKey {
	return keysByType(priorityQOperator.Db, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Like(pattern string) []RenaiKey {
	return keysLike(priorityQOperator.Db, pattern, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Regexp(pattern string) []RenaiKey {
	return keysRegexp(priorityQOperator.Db, pattern, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Values(key string) []RenaiValue {
	return values(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Total() int {
	return total(priorityQOperator.Db, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Size(key string) int {
	return size(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Expire(key string, expireSeconds int) {
	expire(priorityQOperator.Db, key, expireSeconds, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Exists(key string) bool {
	return exists(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Contains(key string, value any) bool {
	return contains(priorityQOperator.Db, key, value, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Clear(key string) int {
	return clear(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Remove(key string, value any) bool {
	return remove(priorityQOperator.Db, key, value, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) BatchRemove(key string, values []any) int {
	return batchRemove(priorityQOperator.Db, key, values, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Delete(key string) {
	delete(priorityQOperator.Db, key, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) BatchDelete(keys []string) {
	batchDelete(priorityQOperator.Db, keys, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Flush() {
	flush(priorityQOperator.Db, PriorityQ)
}

func (priorityQOperator *PriorityQOperator) Members(key string) []common.Tuple2 {
	renaiValues := values(priorityQOperator.Db, key, PriorityQ)
	values := make([]common.Tuple2, 0)
	for _, rv := range renaiValues {
		value := common.NewTuple2(rv.V, rv.Score)
		values = append(values, value)
	}
	return values
}

func (priorityQOperator *PriorityQOperator) Add(key string, value any, score int64) {
	priorityQOperator.commonAdd(key, []common.Tuple2{common.NewTuple2(value, score)}, 1)
}

func (priorityQOperator *PriorityQOperator) BatchAdd(key string, values []common.Tuple2) {
	priorityQOperator.commonAdd(key, values, 1)
}

func (priorityQOperator *PriorityQOperator) AddIfAbsent(key string, value any, score int64) {
	priorityQOperator.commonAdd(key, []common.Tuple2{common.NewTuple2(value, score)}, 2)
}

func (priorityQOperator *PriorityQOperator) BatchAddIfAbsent(key string, values []common.Tuple2) {
	priorityQOperator.commonAdd(key, values, 2)
}

func (priorityQOperator *PriorityQOperator) AddCover(key string, value any, score int64) {
	priorityQOperator.commonAdd(key, []common.Tuple2{common.NewTuple2(value, score)}, 3)
}

func (priorityQOperator *PriorityQOperator) BatchAddCover(key string, values []common.Tuple2) {
	priorityQOperator.commonAdd(key, values, 3)
}

func (priorityQOperator *PriorityQOperator) commonAdd(key string, list []common.Tuple2, flag int) {
	sqlproxy.CallRow(priorityQOperator.Db, common.KV_QUEUE_ADD, key, common.String(list), flag)
}

func (priorityQOperator *PriorityQOperator) Get(key string, idx int, count int) []common.Tuple2 {
	return priorityQOperator.commonGet(key, idx, count, false)
}

func (priorityQOperator *PriorityQOperator) RevGet(key string, idx int, count int) []common.Tuple2 {
	return priorityQOperator.commonGet(key, idx, count, true)
}

func (priorityQOperator *PriorityQOperator) GetByScore(key string, start int64, end int64) []common.Tuple2 {
	rows := priorityQOperator.commonRangeScore(key, start, end, 1)
	return fetchValueAndScore(rows)
}

func (priorityQOperator *PriorityQOperator) commonGet(key string, start int, count int, rev bool) []common.Tuple2 {
	rows := priorityQOperator.commonRange(key, start, count, rev, 1)
	return fetchValueAndScore(rows)
}

func fetchValueAndScore(rows *sql.Rows) []common.Tuple2 {
	defer rows.Close()
	values := make([]common.Tuple2, 0)
	for rows.Next() {
		var value sql.NullString
		var score sql.NullInt64
		err := rows.Scan(&value, &score)
		common.PanicErr(err)
		values = append(values, common.NewTuple2(value.String, score.Int64))
	}
	return values
}

func (priorityQOperator *PriorityQOperator) Pop(key string, idx int, count int) []common.Tuple2 {
	return priorityQOperator.commonPop(key, idx, count, false)
}

func (priorityQOperator *PriorityQOperator) RevPop(key string, idx int, count int) []common.Tuple2 {
	return priorityQOperator.commonPop(key, idx, count, true)
}

func (priorityQOperator *PriorityQOperator) PopByScore(key string, start int64, end int64) []common.Tuple2 {
	rows := priorityQOperator.commonRangeScore(key, start, end, 3)
	return fetchValueAndScore(rows)
}

func (priorityQOperator *PriorityQOperator) commonPop(key string, start int, count int, rev bool) []common.Tuple2 {
	rows := priorityQOperator.commonRange(key, start, count, rev, 3)
	return fetchValueAndScore(rows)
}

func (priorityQOperator *PriorityQOperator) DeleteValue(key string, idx int, count int) int {
	return priorityQOperator.commonDelete(key, idx, count, false)
}

func (priorityQOperator *PriorityQOperator) RevDeleteValue(key string, idx int, count int) int {
	return priorityQOperator.commonDelete(key, idx, count, true)
}

func (priorityQOperator *PriorityQOperator) DeleteByScore(key string, start int64, end int64) int {
	rows := priorityQOperator.commonRangeScore(key, start, end, 2)
	return fetchUpdated(rows)
}

func (priorityQOperator *PriorityQOperator) commonDelete(key string, start int, count int, rev bool) int {
	rows := priorityQOperator.commonRange(key, start, count, rev, 2)
	return fetchUpdated(rows)
}

func (priorityQOperator *PriorityQOperator) commonRange(key string, start int, count int, rev bool, flag int) *sql.Rows {
	r := 1
	if !rev {
		r = -1
	}
	return sqlproxy.CallRows(priorityQOperator.Db, common.KV_QUEUE_RANGE, key, start, count, r, flag)
}

func (priorityQOperator *PriorityQOperator) commonRangeScore(key string, start int64, end int64, flag int) *sql.Rows {
	return sqlproxy.CallRows(priorityQOperator.Db, common.KV_QUEUE_RANGE_SCORE, key, start, end, flag)
}

func (priorityQOperator *PriorityQOperator) Score(key string, value any) []int64 {
	s := "SELECT score FROM renai_kv_queue_value_valid WHERE k = ? AND v = ? ORDER BY score"
	rows := sqlproxy.QueryRows(priorityQOperator.Db, s, key, common.String(value))
	return fetchOnlyScore(rows)
}

func fetchOnlyScore(rows *sql.Rows) []int64 {
	defer rows.Close()
	values := make([]int64, 0)
	for rows.Next() {
		var value sql.NullInt64
		err := rows.Scan(&value)
		common.PanicErr(err)
		values = append(values, value.Int64)
	}
	return values
}

func (priorityQOperator *PriorityQOperator) MinScore(key string, value any) int64 {
	scores := priorityQOperator.Score(key, common.String(value))
	return common.ArrayMin64(scores)
}

func (priorityQOperator *PriorityQOperator) MaxScore(key string, value any) int64 {
	scores := priorityQOperator.Score(key, common.String(value))
	return common.ArrayMax64(scores)
}

func (priorityQOperator *PriorityQOperator) commonRank(key string, value any, rev bool) []int {
	r := 1
	if !rev {
		r = -1
	}
	rows := sqlproxy.CallRows(priorityQOperator.Db, common.KV_QUEUE_RANK, key, common.String(value), r)
	return fetchRank(rows)
}

func (priorityQOperator *PriorityQOperator) Rank(key string, value any) []int {
	return priorityQOperator.commonRank(key, value, false)
}

func (priorityQOperator *PriorityQOperator) HighestRank(key string, value any) int {
	ranks := priorityQOperator.commonRank(key, value, false)
	return common.ArrayMin(ranks)
}

func (priorityQOperator *PriorityQOperator) LowestRank(key string, value any) int {
	ranks := priorityQOperator.commonRank(key, value, false)
	return common.ArrayMax(ranks)
}

func (priorityQOperator *PriorityQOperator) RevRank(key string, value any) []int {
	return priorityQOperator.commonRank(key, value, true)
}

func (priorityQOperator *PriorityQOperator) RevHighestRank(key string, value any) int {
	ranks := priorityQOperator.commonRank(key, value, true)
	return common.ArrayMin(ranks)
}

func (priorityQOperator *PriorityQOperator) RevLowestRank(key string, value any) int {
	ranks := priorityQOperator.commonRank(key, value, true)
	return common.ArrayMax(ranks)
}

func (priorityQOperator *PriorityQOperator) Incr(key string, value any, score int64) []int64 {
	rows := sqlproxy.CallRows(priorityQOperator.Db, common.KV_QUEUE_INCR, key, common.String(value), score)
	return fetchOnlyScore(rows)
}
